package discoveryServer;
import discovery.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import node.NodeDescriptor;

public class DiscoveryProxyServant implements Runnable {

	private DatagramPacket receivePacket = null;
	private DatagramSocket responseSocket = null;
	private Discovery proxy = null;

	
	private boolean ackSet=false;
	
	public DiscoveryProxyServant(DatagramPacket pack,Discovery proxyServer){		
		super();
		this.receivePacket = pack;
		this.proxy=proxyServer;
	}
	

	
	public void run(){				
		
		try{	
			System.out.println(Thread.currentThread().toString());
			ByteArrayInputStream bytein=new ByteArrayInputStream(receivePacket.getData());
			ObjectInputStream inCode = new ObjectInputStream(bytein);
			String code = inCode.readUTF();
			inCode.close();
			
			responseSocket = new DatagramSocket();
			
			NodeDescriptor descriptor=null;
			Object obj = null;
			int result = 10;
			int ack = 0;
			
			if(code.equalsIgnoreCase("DAS")){
				//logica per mandare ack ok
				sendAck(ack);
				ackSet=true;
				obj = (NodeDescriptor)receiveObject();
				if(obj!=null){
					synchronized(((DiscoveryProxyServer)proxy)){
						result =((DiscoveryProxyServer)proxy).addNode((NodeDescriptor)obj);
						System.out.println("add result: "+result);
						sendAck(result);   // scrive un intero
					}
					System.out.println(((NodeDescriptor)obj).getIp());					
				}
			}
			
			if(code.equalsIgnoreCase("DUS")){
				sendAck(ack);
				ackSet=true;
				obj = (NodeDescriptor)receiveObject();
				if(obj!=null){
					synchronized(((DiscoveryProxyServer)proxy)){
						result =((DiscoveryProxyServer)proxy).updateNode((NodeDescriptor)obj);
						System.out.println("update result: "+result);
						sendAck(result);   // scrive un intero
					}					
					System.out.println(((NodeDescriptor)obj).getIp());
				}
			}
			
			if(code.equalsIgnoreCase("DDS")){
				sendAck(ack);
				ackSet=true;
				obj = (NodeDescriptor)receiveObject();
				if(obj!=null){
					synchronized(((DiscoveryProxyServer)proxy)){
						result =((DiscoveryProxyServer)proxy).delNode((NodeDescriptor)obj);
						System.out.println("delete result: "+result);
						sendAck(result);   // scrive un intero
					}					
					System.out.println(((NodeDescriptor)obj).getIp());
				}
			}
			
			if(code.equalsIgnoreCase("DLS")){
				sendAck(ack);
				ackSet=true;
				obj = (String)receiveObject();
				if(obj!=null){
					synchronized(((DiscoveryProxyServer)proxy)){
						descriptor =((DiscoveryProxyServer)proxy).lookup((String)obj);
						if(descriptor !=null){
							System.out.println("lookup result: "+ descriptor.getIp());
							System.out.println(descriptor.getIp());							
						}
						else
							System.out.println("lookup nussun result");
						
						sendObject(descriptor);   // scrive un intero
					}					
				}
			}
								
		}
		catch(Exception e){
			e.printStackTrace();
		}
		finally {
		    if (!responseSocket.isClosed()) { 
		        System.out.println("Closing response soscket");
		        responseSocket.close(); 
		    } else { 
		        System.out.println("Open response socket");
		    } 
		} 
		
	}
	
	
	public void sendAck(int ack){
	try{
		ByteArrayOutputStream byteStrem = new ByteArrayOutputStream ();
		DataOutputStream dos = new DataOutputStream (byteStrem);			
		dos.writeInt(ack);
		dos.flush();
		dos.close();
				
		byte[] sendData=byteStrem.toByteArray();
	
		InetAddress returnIPAddress = receivePacket.getAddress();
		int port = receivePacket.getPort();
	
		DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, returnIPAddress, port); 		
		responseSocket.send(sendPacket);
		
		if(!ackSet)
			System.out.println("ack inviato : "+ack);
			else
				System.out.println("Risultato inviato: "+ack);	
	}
	catch (UnknownHostException ex) { 
	     System.err.println(ex);
	}
    catch (IOException ex) {
	     System.err.println(ex);
	}
	
	}

	public Object receiveObject(){
	 
			try {
				responseSocket.setSoTimeout(NodeDescriptor.getInstance().getD_timeout());			
				responseSocket.receive(receivePacket); 	         
	            ByteArrayInputStream InbyteStream=new ByteArrayInputStream(receivePacket.getData());
			    ObjectInputStream ois=new ObjectInputStream(InbyteStream);
				Object tmp = ois.readObject();
				ois.close();
				System.out.println("Object "+tmp);
				return tmp;
		  }
	   catch (SocketTimeoutException ste){
		   System.out.println ("Timeout Occurred: Packet assumed lost");
	   	  } 
	   catch (IOException e) {
		   e.printStackTrace();
	      } 
	   catch (ClassNotFoundException e) {
		   e.printStackTrace();
	      }
		//responseSocket.close(); 
		return null;
	}
	
	public void sendObject(NodeDescriptor local){

		try {			  		      
			  ByteArrayOutputStream byteStrem=new ByteArrayOutputStream();
			  ObjectOutputStream out=new ObjectOutputStream(byteStrem);
			  out.writeObject(local);
			  out.flush();
			  out.close();
			  byte[] sendData=byteStrem.toByteArray();
						 	
		      DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, this.receivePacket.getAddress(), this.receivePacket.getPort()); 
		      responseSocket.send(sendPacket); 
		      System.out.println("Send NodeDescriptor");	
			}
			catch (UnknownHostException ex) { 
		     System.err.println(ex);
		    }
		   catch (IOException ex) {
		     System.err.println(ex);
		    }
	}	
	
}

